import * as ts from 'typescript';
import { FileUtils } from './FileUtils';

/**
 * 类型解析工具类
 */
export class TypeUtils {
  /**
   * 创建TypeScript程序
   * @param filePath 文件路径
   * @returns TypeScript程序
   */
  public static createProgram(filePath: string): ts.Program {
    const compilerOptions: ts.CompilerOptions = {
      allowJs: true,
      checkJs: true,
      target: ts.ScriptTarget.ESNext,
      module: ts.ModuleKind.ESNext,
      moduleResolution: ts.ModuleResolutionKind.NodeJs,
      esModuleInterop: true,
      strict: false,
      skipLibCheck: true,
      jsx: ts.JsxEmit.Preserve,
    };

    return ts.createProgram([filePath], compilerOptions);
  }

  /**
   * 解析TypeScript类型为字符串表示
   * @param type TypeScript类型
   * @param typeChecker 类型检查器
   * @returns 类型的字符串表示
   */
  public static typeToString(type: ts.Type, typeChecker: ts.TypeChecker): string {
    if (type.isUnion()) {
      return (type as ts.UnionType).types.map(t => this.typeToString(t, typeChecker)).join(' | ');
    }
    
    if (type.isIntersection()) {
      return (type as ts.IntersectionType).types.map(t => this.typeToString(t, typeChecker)).join(' & ');
    }
    
    // 对于字面量类型，返回实际值
    if ((type.flags & ts.TypeFlags.StringLiteral) !== 0) {
      return `'${(type as ts.StringLiteralType).value}'`;
    }
    
    if ((type.flags & ts.TypeFlags.NumberLiteral) !== 0) {
      return `${(type as ts.NumberLiteralType).value}`;
    }
    
    if ((type.flags & ts.TypeFlags.BooleanLiteral) !== 0) {
      // 由于无法直接访问布尔字面量的值，我们使用typeChecker
      return typeChecker.typeToString(type);
    }
    
    return typeChecker.typeToString(type);
  }

  /**
   * 分析变量的类型
   * @param node 变量声明节点
   * @param program TypeScript程序
   * @returns 变量类型的字符串表示
   */
  public static analyzeVariableType(node: ts.VariableDeclaration, program: ts.Program): string {
    const typeChecker = program.getTypeChecker();
    const type = typeChecker.getTypeAtLocation(node);
    
    return this.typeToString(type, typeChecker);
  }

  /**
   * 分析函数的返回类型
   * @param node 函数声明节点
   * @param program TypeScript程序
   * @returns 函数返回类型的字符串表示
   */
  public static analyzeFunctionReturnType(node: ts.FunctionDeclaration | ts.MethodDeclaration | ts.ArrowFunction, program: ts.Program): string {
    const typeChecker = program.getTypeChecker();
    const signature = typeChecker.getSignatureFromDeclaration(node as any);
    
    if (!signature) {
      return 'any';
    }
    
    const returnType = typeChecker.getReturnTypeOfSignature(signature);
    return this.typeToString(returnType, typeChecker);
  }

  /**
   * 分析参数类型
   * @param node 参数声明节点
   * @param program TypeScript程序
   * @returns 参数类型的字符串表示
   */
  public static analyzeParameterType(node: ts.ParameterDeclaration, program: ts.Program): string {
    const typeChecker = program.getTypeChecker();
    const type = typeChecker.getTypeAtLocation(node);
    
    return this.typeToString(type, typeChecker);
  }

  /**
   * 从类型声明中解析可能的枚举值
   * @param type TypeScript类型
   * @param typeChecker 类型检查器
   * @returns 可能的枚举值数组
   */
  public static extractPossibleEnumValues(type: ts.Type, typeChecker: ts.TypeChecker): string[] {
    if (!type.isUnion()) {
      return [];
    }
    
    const values: string[] = [];
    
    (type as ts.UnionType).types.forEach(t => {
      if ((t.flags & ts.TypeFlags.StringLiteral) !== 0) {
        values.push(`'${(t as ts.StringLiteralType).value}'`);
      } else if ((t.flags & ts.TypeFlags.NumberLiteral) !== 0) {
        values.push(`${(t as ts.NumberLiteralType).value}`);
      }
    });
    
    return values;
  }

  /**
   * 从字符串类型表示中提取可能的枚举值
   * @param typeString 类型字符串表示
   * @returns 可能的枚举值数组
   */
  public static extractEnumValuesFromTypeString(typeString: string): string[] {
    // 处理类似 'value1' | 'value2' | 'value3' 的类型
    const values: string[] = [];
    const matches = [...typeString.matchAll(/'([^']+)'|"([^"]+)"/g)];
    
    for (const match of matches) {
      const value = match[1] || match[2];
      if (value) {
        values.push(value);
      }
    }
    
    return values;
  }

  /**
   * 合并从多个来源获取的类型信息
   * @param derivedType 自动推导的类型
   * @param commentType 从注释中提取的类型
   * @returns 合并后的类型字符串
   */
  public static mergeTypes(derivedType: string | null | undefined, commentType: string | null | undefined): string {
    if (!derivedType && !commentType) {
      return 'any';
    }
    
    if (!derivedType) {
      return commentType || 'any';
    }
    
    if (!commentType) {
      return derivedType;
    }
    
    // 如果两种类型相同，或者注释类型是派生类型的子集，使用派生类型
    if (derivedType === commentType || derivedType.includes(commentType)) {
      return derivedType;
    }
    
    // 如果派生类型是 'any'，使用注释类型
    if (derivedType === 'any') {
      return commentType;
    }
    
    // 否则合并两种类型
    return `${derivedType} | ${commentType}`;
  }
} 